package com.fis.vaadin.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import java.util.Vector;

import com.fis.mca.admin.AppClient;
import com.fis.vaadin.VaadinForm;
import com.fis.vaadin.component.CommonButtonPanel;
import com.fis.vaadin.component.CommonDialog;
import com.fis.vaadin.component.ConfirmDeletionDialog;
import com.fis.vaadin.component.CustomTable;
import com.fis.vaadin.component.TableContainer;
import com.fis.vaadin.util.FormUtil;
import com.fis.vaadin.util.MessageAlerter;
import com.fis.vaadin.validator.PanelActionProvider;
import com.vaadin.data.Container;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Field;
import com.vaadin.ui.Form;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseEvent;
import com.vaadin.ui.Window.Notification;

import eu.livotov.tpt.gui.dialogs.OptionDialog.OptionDialogResultListener;
import eu.livotov.tpt.gui.dialogs.OptionKind;
import eu.livotov.tpt.i18n.TM;

/**
 * @author VinhNDQ
 * 
 * @Company FPT-FIS-TES
 * 
 * @date Dec 11, 2012
 */
public abstract class BaseTableForm<FormLayout extends VaadinForm, PK, T>
		extends VerticalLayout implements PanelActionProvider,
		OptionDialogResultListener {

	private static final long serialVersionUID = 1L;
	protected FormLayout formLayout;
	protected Form form;
	protected BaseDefaultFieldFactory fieldFactory;
	private T defaultValue;
	private TableContainer container;
	private CustomTable tbl;
	private CommonButtonPanel pnlAction;
	private CommonDialog dialog;
	BeanItemContainer<T> beanData;

	private int total = 0;
	private ArrayList<T> canDelete = new ArrayList<T>();
	private ConfirmDeletionDialog confirm;
	private Object[] visibleProperties;

	public BaseTableForm() {
		try {
			this.formLayout = getLayoutDialog();
			this.defaultValue = getDefaultValue();
			beanData = new BeanItemContainer<T>(
					getClassPersistent(defaultValue));
			if (formLayout != null) {
				HashMap<String, Field> mapFieldData = this.formLayout
						.getFormData().getMapFieldData();
				if (mapFieldData != null) {
					Set<String> setProperty = mapFieldData.keySet();
					if (setProperty != null)
						visibleProperties = setProperty.toArray();
				}
			}
			initLayout();
		} catch (Exception ex) {
			ex.printStackTrace();
			getWindow().showNotification(ex.getMessage(),
					Notification.TYPE_ERROR_MESSAGE);
		}

	}

	@SuppressWarnings("unchecked")
	private Class<? super T> getClassPersistent(T value) {
		return (Class<? super T>) value.getClass();
	}

	public abstract void initTableColumn(Table tbl);

	public abstract String[] getFilterColumns();

	public abstract FormLayout getLayoutDialog() throws Exception;

	public abstract T getDefaultValue() throws Exception;

	public abstract Collection<T> loadData() throws Exception;

	public abstract PK insert(T value) throws Exception;

	public abstract void update(T value) throws Exception;

	public abstract void delete(T value) throws Exception;

	public abstract String getTableName() throws Exception;

	public abstract String getConditionUnique(T value) throws Exception;

	public abstract String getCaptionNameI18N() throws Exception;

	public abstract T setPKValue(T newValue, PK pk) throws Exception;

	private void initLayout() throws Exception {
		initComponent();
		this.setSizeFull();
		this.addComponent(container);
		this.setExpandRatio(container, 1f);
		this.setComponentAlignment(container, Alignment.TOP_CENTER);
	}

	private void initComponent() throws Exception {
		pnlAction = new CommonButtonPanel(this);
		initTable();
		initForm();
	}

	private void initForm() throws Exception {
		form = new BaseFormDetail(formLayout);
		form.setWriteThrough(false);
		form.setInvalidCommitted(false);
		form.setImmediate(false);
		fieldFactory = new BaseDefaultFieldFactory(this.formLayout);
		form.setFormFieldFactory(fieldFactory);

		dialog = new CommonDialog(StringUtil.nvl(getCaptionNameI18N(), ""),
				form, this);
		initDialog(dialog);
		dialog.addListener(new Window.CloseListener() {
			/**
			 * 
			 */
			private static final long serialVersionUID = 4896156626083193950L;

			@Override
			public void windowClose(CloseEvent e) {
				pnlAction.clearAction();
			}
		});
	}

	protected void initDialog(CommonDialog dialog) {
		dialog.setWidth("400px");
		dialog.setHeight("300px");
	}

	public String tableFormatPropertyValue(Object rowId, Object colId,
			Property property) {
		return null;
	}

	@SuppressWarnings("serial")
	private void initTable() throws Exception {
		tbl = new CustomTable("", beanData) {
			@Override
			protected String formatPropertyValue(Object rowId, Object colId,
					Property property) {
				String strFormat = tableFormatPropertyValue(rowId, colId,
						property);
				if (strFormat != null) {
					return strFormat;
				} else
					return super.formatPropertyValue(rowId, colId, property);
			}
		};

		tbl.setMultiSelect(false);
		tbl.setImmediate(true);

		tbl.addListener(new Property.ValueChangeListener() {
			public void valueChange(ValueChangeEvent event) {
				Object id = tbl.getValue();
				setEnableAction(id);
			}

		});

		tbl.addListener(new Container.ItemSetChangeListener() {
			public void containerItemSetChange(ItemSetChangeEvent event) {
				pnlAction.setRowSelected(false);
			}
		});

		if (getPermission().contains("U")) {
			tbl.addListener(new ItemClickEvent.ItemClickListener() {
				private static final long serialVersionUID = 2068314108919135281L;

				public void itemClick(ItemClickEvent event) {
					if (event.isDoubleClick()) {
						pnlAction.edit();
					}
				}
			});
		}

		initTableColumn(tbl);

		if (tbl.getContainerDataSource().equals(null)) {
			pnlAction.setRowSelected(false);
		}

		container = new TableContainer(tbl);
		container.setFilteredColumns(getFilterColumns());
		container.setActionPanel(pnlAction);
	}

	private Window createDialog(Item item) throws Exception {
		form.setItemDataSource(item);
		if (visibleProperties != null)
			form.setVisibleItemProperties(visibleProperties);
		form.setValidationVisible(false);
		// form.focus();
		if (formLayout.getFormData().getMapFieldData().size() > 0) {
			formLayout
					.getFormData()
					.getMapFieldData()
					.get(formLayout.getFormData().getMapFieldData().keySet()
							.iterator().next()).focus();
		}
		getWindow().addWindow(dialog);
		return dialog;
	}

	@SuppressWarnings("unchecked")
	public void showDialog() {
		try {
			if (getWindow().getChildWindows().contains(dialog)) {
				return;
			}

			Item item = null;
			int action = pnlAction.getAction();

			if (action == PanelActionProvider.ACTION_EDIT) {
				T selectedValue = (T) tbl.getSelectedValue();
				if (selectedValue == null) {
					pnlAction.clearAction();
					return;
				}
				Class<?> c = selectedValue.getClass();
				T newBean = (T) c.newInstance();
				java.lang.reflect.Field fields[] = c.getDeclaredFields();
				for (java.lang.reflect.Field field : fields) {
					field.setAccessible(true);
					field.set(newBean, field.get(selectedValue));
				}
				item = tbl.getItem(selectedValue);
				fieldFactory.setOldValue(newBean);
			} else if (action == PanelActionProvider.ACTION_ADD_COPY) {
				T current = (T) tbl.getSelectedValue();
				if (current == null) {
					pnlAction.clearAction();
					return;
				}
				Class<?> c = current.getClass();
				T newBean = (T) c.newInstance();
				java.lang.reflect.Field fields[] = c.getDeclaredFields();
				for (java.lang.reflect.Field field : fields) {
					field.setAccessible(true);
					field.set(newBean, field.get(current));
				}
				item = new BeanItem<T>(newBean);
				fieldFactory.setOldValue(null);
			} else {
				item = new BeanItem<T>(this.defaultValue);
				fieldFactory.setOldValue(null);
			}
			createDialog(item);
		} catch (Exception ex) {
			ex.printStackTrace();
			// MessageAlerter.showErrorMessage(getWindow(), ex.getMessage());
			getWindow().showNotification(ex.getMessage(),
					Notification.TYPE_ERROR_MESSAGE);
		}

	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void accept() {
		try {
			boolean modified = form.isModified();
			if (pnlAction.getAction() == PanelActionProvider.ACTION_EDIT
					&& !modified) {
				pnlAction.clearAction();
				return;
			}

			form.commit();
			BeanItem<T> beanItem = null;
			beanItem = (BeanItem<T>) form.getItemDataSource();
			T newOject = beanItem.getBean();
			try {
				String strLogID = getLogID();
				if (pnlAction.getAction() == PanelActionProvider.ACTION_ADD
						|| pnlAction.getAction() == PanelActionProvider.ACTION_ADD_COPY) {
					try {

						PK id = insert(newOject);
						LogUtil.logAfterInsert(strLogID, getTableName(),
								getConditionUnique(newOject));

						newOject = setPKValue(newOject, id);
						if (id != null) {
							tbl.addItem(newOject);
							tblSetARowSelect(newOject);

							MessageAlerter.showMessageI18n(getWindow(), TM.get(
									"common.msg.add.success",
									StringUtil.nvl(getCaptionNameI18N(), "")
											.toLowerCase()));
						} else {
							MessageAlerter.showErrorMessage(getWindow(), TM
									.get("common.msg.add.fail",
											StringUtil.nvl(
													getCaptionNameI18N(), "")
													.toLowerCase()));
						}
					} catch (Exception e) {
						LogUtil.logActionFailed(strLogID);
						if (e.getMessage().contains("ORA-00001")) {
							MessageAlerter.showErrorMessage(getWindow(),
									TM.get("ORA-00001"));
						} else
							MessageAlerter.showErrorMessage(getWindow(),
									e.getMessage());
						e.printStackTrace();

					}
				} else if (pnlAction.getAction() == PanelActionProvider.ACTION_EDIT) {
					try {

						Vector oldValue = LogUtil.logBeforeUpdate(strLogID,
								getTableName(), getConditionUnique(newOject));
						update(newOject);

						LogUtil.logAfterUpdate(oldValue);

						tblSetARowSelect(newOject);

						MessageAlerter.showMessageI18n(getWindow(), TM.get(
								"common.msg.edit.success",
								StringUtil.nvl(getCaptionNameI18N(), "")
										.toLowerCase()));

					} catch (Exception e) {
						LogUtil.logActionFailed(strLogID);
						e.printStackTrace();
						MessageAlerter.showErrorMessage(getWindow(),
								e.getMessage());

					}
				}
			} catch (Exception e) {
				MessageAlerter.showErrorMessage(getWindow(), e.getMessage());
				e.printStackTrace();
			}

			pnlAction.clearAction();
			FormUtil.clearCache(null);
		} catch (Exception ex) {
			ex.printStackTrace();
			MessageAlerter.showErrorMessage(getWindow(), ex.getMessage());
		}
	}

	@Override
	public String getPermission() {
		return AppClient.getPermission(this.getClass().getName());
	}

	public void loadDataForTable() {
		try {
			beanData.removeAllItems();
			beanData.addAll(loadData());
		} catch (Exception e) {
			MessageAlerter.showErrorMessage(
					getWindow(),
					TM.get("common.loaddata.fromdb.fail") + "</br>"
							+ e.getMessage());
			e.printStackTrace();
		}
	}

	public CustomTable getCustomTable() {
		return tbl;
	}

	@SuppressWarnings("unchecked")
	public void delete() {
		Set<T> set = (Set<T>) tbl.getCollectionValue();
		resetResource();

		for (T obj : set) {
			total++;

			boolean b = false;// valueService.checkConstraints(obj.getCurrencyId());
			if (!b) {
				canDelete.add(obj);
			}
		}

		if (canDelete.size() == 0) {
			MessageAlerter.showErrorMessageI18n(getWindow(),
					TM.get("common.msg.delete.contrain"));
		} else {
			String message = TM.get("common.msg.delete.confirm");
			confirmDeletion(message);
		}
	}

	private void resetResource() {
		canDelete.clear();
		total = 0;
	}

	private void confirmDeletion(String message) {
		if (confirm == null) {
			confirm = new ConfirmDeletionDialog(getApplication());
		}
		confirm.show(message, this);
	}

	private void doDelete() {
		String strLogID = getLogID();
		try {
			int deleted = 0;
			for (T value : canDelete) {

				LogUtil.logBeforeDelete(strLogID, getTableName(),
						getConditionUnique(value));

				delete(value);
				tbl.removeItem(value);
				deleted++;
			}
			MessageAlerter.showMessageI18n(getWindow(),
					TM.get("message.delete"), deleted, total);

		} catch (Exception e) {
			LogUtil.logActionFailed(strLogID);
			MessageAlerter.showErrorMessage(getWindow(), e.getMessage());
			e.printStackTrace();
		}
	}

	private void setEnableAction(Object id) {
		form.setItemDataSource(tbl.getItem(id));
		pnlAction.setRowSelected(id != null);
	}

	private void tblSetARowSelect(Object id) {
		tbl.setMultiSelect(false);
		tbl.select(id);
		tbl.setMultiSelect(false);
	}

	@Override
	public void dialogClosed(OptionKind option) {
		if (OptionKind.OK.equals(option)) {
			if (canDelete != null && canDelete.size() > 0) {
				doDelete();
			}
		}
	}

	@Override
	public void export() {

	}

	private String getLogID() {
		try {
			String strLogID = LogUtil.getLogID(getApplication(), "", this
					.getClass().getName(), pnlAction);
			return strLogID;
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;

		}
	}
}
